/* Copyright © 2010 LPRM
Universidade Federal do Espírito Santo - RNP-GT CWTools

This file is part of GW-Android.

GW-Android is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

GW-Android is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with GW-Android. If not, see <http://www.gnu.org/licenses/>. */
package br.ufes.cwtools.gw.android;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import android.app.Activity;
import android.app.Service;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import br.ufes.cwtools.gw.android.components.Component;
import br.ufes.cwtools.gw.android.components.ComponentDefinition;
import br.ufes.cwtools.gw.android.connection_service.ConnectionServiceConnection;
import br.ufes.cwtools.gw.android.connection_service.IConnectionService;
import br.ufes.cwtools.gw.android.location_service.ILocationService;

public class MainActivity extends Activity { 
	private static final String LOG_TAG = "MainActivity";

	private static final int REQUEST_CODE_FIRST_ACTION = 100;
	private static final int LOGO_MSG = 1;
	private static final int LOCATION_SERVICE_DEFAULT_REPORTING_PERIOD = 60000;
	
	private ApplicationConfigurationParser parser;
	private ArrayList<ComponentDefinition> components;
	private Map<String, ComponentDefinition> componentsMap;
	private ArrayList<String> componentsStack;
	private String applicationXml;
	private String applicationUrl;
	
	private LocationServiceConnection locationServiceConnection;
	private ServiceConnection logoConnection;
	
	@SuppressWarnings("unchecked")
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		
		setContentView(R.layout.base);
		
		applicationUrl = getIntent().getStringExtra(Component.EXTRA_APPLICATION_URL);
		applicationXml = getIntent().getStringExtra(Component.EXTRA_APPLICATION_XML);
		if (applicationXml == null) {
			Log.e(LOG_TAG, "XML de definições aplicação não foi fornecido como extra.");
			finish();
			return;
		}
		
		// Carrega os componentes da aplicação.
		Object retained = getLastNonConfigurationInstance();
		if (retained instanceof ArrayList<?>) {
			components = (ArrayList<ComponentDefinition>) retained;
		}
		if (components == null) {
			components = loadComponents();
		}
		
		componentsMap = new HashMap<String, ComponentDefinition>();
		
		// Monta os botões do menu.
		LinearLayout container = (LinearLayout) findViewById(R.id.buttons_container);
		for (ComponentDefinition component : components) {
			componentsMap.put(component.getTag(), component);
			
			if (component.showInMenu()) {
				View view = getLayoutInflater().inflate(R.layout.action_menu_item, null);
				container.addView(view);
				
				Button button = (Button) view.findViewById(R.id.button);
				button.setText(component.getLabel());
				button.setTag(component.getTag());
			}
		}
		
		componentsStack = new ArrayList<String>();
		parser = new ApplicationConfigurationParser();

		setupLayout();
		
		logoConnection = new ConnectionServiceConnection(MainActivity.class.getName()) {
			@Override
			public void onRequestFinished(String id, String filePath) {
				Log.d(LOG_TAG, "Logo download callback.");
				// Não podemos atualizar a UI diretamente aqui.
				// Envia mensagem para o handler que atualizará o logo.
				mLogoHandler.sendMessage(mLogoHandler.obtainMessage(LOGO_MSG, filePath));
				unbindService(logoConnection);
			}

			@Override
			public void onServiceConnected() {
				try {
					registerGetRequest(parser.getLogoImage(),
							ConnectionServiceConnection.generateId(), true);
				} catch (RemoteException e) {
					Log.e(LOG_TAG, "Erro no download da logo.", e);
				}
			}
		};
		// Download logo image.
		if (parser.getLogoImage() != null) {
			bindService(new Intent(IConnectionService.class.getName()), logoConnection, Context.BIND_AUTO_CREATE);
		}
		
		//Bind do location service.
		locationServiceConnection = new LocationServiceConnection();
		bindService(new Intent(ILocationService.class.getName()), locationServiceConnection, Service.BIND_AUTO_CREATE);
	}
	
	@Override
	protected void onDestroy() {
		super.onDestroy();
		
		try{
			locationServiceConnection.stopLocationReporting();
			unbindService(locationServiceConnection);
		}catch(Exception e){
			Log.e(LOG_TAG, "Erro no onDestroy(). " + e.getMessage());
		}
	}
	
	private void setupLayout() {
		final String source = applicationXml;
		try {
			parser.parse(source);
			if (parser.getDisplayName() != null) {
				((TextView) findViewById(R.id.title)).setText(parser.getDisplayName());
			}
			if (parser.getTextColor() != null) {
				try {
					((TextView) findViewById(R.id.title)).setTextColor(Color.parseColor(parser.getTextColor()));
				} catch (IllegalArgumentException e) {
					Log.w(LOG_TAG, "Invalid format provided for textColor on application XML. Check Color.parseColor() for valid color formats.");
				}
			}
			if (parser.getBackgroundColor() != null) {
				try {
					findViewById(R.id.base).setBackgroundColor(Color.parseColor(parser.getBackgroundColor()));
				} catch (IllegalArgumentException e) {
					Log.w(LOG_TAG, "Invalid format provided for backgroundColor on application XML. Check Color.parseColor() for valid color formats.");
				}
			}
			
		} catch (Exception e) {
			Log.d(LOG_TAG, "Exception while parsing application XML to obtain application layout.");
		}
	}
	
	// Handler para atualizar o logo.
	private Handler mLogoHandler = new Handler() {
		@Override
		public void handleMessage(Message msg) {
			switch (msg.what) {
			case LOGO_MSG:
				final String filePath = (String) msg.obj;
				Drawable d = Drawable.createFromPath(filePath);
				ImageView v = (ImageView) findViewById(R.id.logo);
				v.setImageDrawable(d);
				v.setVisibility(View.VISIBLE); // Torna a imagem de logo visível...
				findViewById(R.id.title).setVisibility(View.GONE); // ...e esconde o título. 
				break;
			default:
				super.handleMessage(msg);
			}
		}
	};
	
	@Override
	public Object onRetainNonConfigurationInstance() {
		// Guarda o estado atual em caso de uma mudança na orientação da tela;
		// este método não tratará os casos em que esta atividade é terminada por alguma razão.
		return components;
	}
	
	@Override
	protected void onSaveInstanceState(Bundle outState) {
		outState.putStringArrayList("componentsStack", componentsStack);
	}
	
	@Override
	protected void onRestoreInstanceState(Bundle savedInstanceState) {
		componentsStack = savedInstanceState.getStringArrayList("componentsStack");
	}
	
	ArrayList<ComponentDefinition> loadComponents() {
		return ComponentsParser.parse(applicationXml);
	}
	
	private ComponentDefinition getComponent(String tag) {
		return componentsMap.get(tag);
	}
	
	public void buttonOnClick(View view) {
		Object viewTag = view.getTag();
		if (viewTag instanceof String) {
			componentsStack.clear();
			startComponentActivity(this.getIntent().getExtras(), (String) viewTag);
		} else {
			Log.e(LOG_TAG, "Não foi possível determinar o componente associado a este botão.");
			return;
		}
	}
	
	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		super.onActivityResult(requestCode, resultCode, data);

		String tag = null;
		switch (resultCode) {
		case RESULT_OK:
			Log.d(LOG_TAG, "Activity terminou com RESULT_OK.");
			if (requestCode == REQUEST_CODE_FIRST_ACTION) {
				tag = data.getStringExtra(Component.EXTRA_SELECTED_COMPONENT_TAG);
				data.removeExtra(Component.EXTRA_SELECTED_COMPONENT_TAG);
				if (tag == null) {
					Log.d(LOG_TAG, "Não foi retornado um extra informando o componente seguinte.");	
				}
			} else {
				Log.w(LOG_TAG, "Activity retornou com request code não esperado.");
			}
			break;
		case RESULT_CANCELED:
			Log.d(LOG_TAG, "Activity terminou com RESULT_CANCELED.");
			break;
		case Component.RESULT_BACK:
			Log.d(LOG_TAG, "Activity terminou com RESULT_BACK.");
			final int size = componentsStack.size();
			componentsStack.remove(size - 1);  // O topo da pilha é o componente que retornou.
			if (size == 1) Log.d(LOG_TAG, "Não há componentes anteriores na pilha.");
			else tag = componentsStack.remove(size - 2);
			break;
		}
		if (tag != null && !tag.equals("")) startComponentActivity(data.getExtras(), tag);
		else Log.d(LOG_TAG, "Voltamos ao menu principal.");
	}
	
	private void startComponentActivity(Bundle data, String tag) {
		final ComponentDefinition component = getComponent(tag);
		if (component == null) {
			Log.e(LOG_TAG, "Componente com a tag fornecida não encontrado.");
			return;
		}
		
		final Intent intent = new Intent();
		intent.addCategory(Constants.COMPONENTS_INTENT_CATEGORY);
		intent.setAction(component.getAction());
		if (data != null) intent.putExtras(data);
		intent.putExtras(component.getExtras());
		intent.putExtra(Component.EXTRA_RELATED_COMPONENTS, component.getRelatedComponents());
		intent.putExtra(Component.EXTRA_BASE_URL, component.getBaseUrl());
		Log.d(LOG_TAG, "Inicializando EXTRA_BASE_URL com " + component.getBaseUrl());
		
		Log.d(LOG_TAG, "Iniciando activity para o componente \"" + component.getLabel() + "\".");
		componentsStack.add(component.getTag());
		try {
			startActivityForResult(intent, REQUEST_CODE_FIRST_ACTION);
		} catch (ActivityNotFoundException e) {
			Log.w(LOG_TAG, "ActivityNotFoundException ao iniciar componente.", e);
			Toast.makeText(this, "Componente não encontrado no sistema.", Toast.LENGTH_SHORT).show();
		}
	}

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
    	getMenuInflater().inflate(R.menu.main_menu, menu);
    	return super.onCreateOptionsMenu(menu);
    }
    
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
    	switch (item.getItemId()) {
		case R.id.logout:
			finish();
			return true;
		default:
			return super.onOptionsItemSelected(item);
		}
    }
    
    class LocationServiceConnection implements ServiceConnection {
		private ILocationService ls;
		
		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
			Log.d(LOG_TAG, "Location service bound.");
			ls = ILocationService.Stub.asInterface(service);
			
			for (ComponentDefinition cd : components) {
				if (cd.getPackageName().equals("br.ufes.cwtools.gw.android.components.tracker")) {
					Log.d(LOG_TAG, "Tracker encontrado, inicializando o location service...");
					try {
						Bundle params = new Bundle();
						params.putString("trackingInfo.latitude", "${latitude}");
						params.putString("trackingInfo.longitude", "${longitude}");
						params.putString("trackingInfo.accuracy", "${accuracy}");

						int reportingPeriod;
						String strReportingPeriod = params.getString("reportingPeriod");
						String reportingUrl = params.getString("reportingUrl");
						if(strReportingPeriod == null){
							reportingPeriod = LOCATION_SERVICE_DEFAULT_REPORTING_PERIOD; //default value
						}else{
							reportingPeriod = Integer.parseInt(strReportingPeriod);
						}
						if (reportingUrl == null){
							reportingUrl = cd.getBaseUrl() + "/update";
						}
						
						ls.setReportingPeriod(reportingPeriod);
						ls.setReportingUrl(applicationUrl + reportingUrl);
						ls.setReportingParameters(params);
						ls.startLocationReporting();
					} catch (RemoteException e) {
						Log.w("GPS", "Erro na configuração do auto-update.");
					}
					break;
				}
			}
		}
		
		//Métodos que serão acessados pelo menu
		public void startLocationReporting() {
			if (ls != null) {
				try {
					ls.startLocationReporting();
				} catch (RemoteException e) {
					Log.w(LOG_TAG, "Erro na inicialização do reportingService.", e);
				}
			}
		}
		
		//Métodos que serão acessados pelo menu
		public void stopLocationReporting() {
			if (ls != null) {
				try {
					ls.stopLocationReporting();
				} catch (RemoteException e) {
					Log.w(LOG_TAG, "Erro na finalização do reportingService.", e);
				}
			}
		}

		@Override
		public void onServiceDisconnected(ComponentName name) {
			ls = null;
		}
	};
}
